/*
 * Copyright 2008  Telsima Corp.
 * Authors:  Shu Lin <slin@telsima.com>
 *
 * The user of the list is responsible to make sure multithread safety.
 */

#include "tw_list.h"

void tw_list_init(tw_list_p arg_pList, tw_node_cmp_func nodecmp)
{
    arg_pList->head  = arg_pList->tail = NULL;
    arg_pList->count = 0;
    arg_pList->nodecmp = nodecmp;
}

inline tw_uint32_t tw_list_get_count(tw_list_p arg_pList)
{
    return arg_pList->count;
}

inline void tw_list_set_count(tw_list_p arg_pList, tw_uint32_t count)
{
    arg_pList->count = count;
}

tw_bool tw_list_isempty(tw_list_p arg_pList)
{
    if( arg_pList->count == 0 )
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

void tw_list_addhead(tw_list_p arg_pList, tw_list_node_p arg_pItem)
{
    if( arg_pList->count == 0 )
    {
        arg_pItem->next = NULL;
        arg_pList->head = arg_pItem;
        arg_pList->tail = arg_pItem;
    }
    else
    {
        arg_pItem->next = arg_pList->head;
        arg_pList->head = arg_pItem;
    }

    arg_pList->count++;
}



void tw_list_addtail(tw_list_p arg_pList, tw_list_node_p arg_pItem)
{
    if( arg_pList->count == 0 )
    {
        arg_pItem->next = NULL;
        arg_pList->head = arg_pItem;
        arg_pList->tail = arg_pItem;
    }
    else
    {
        arg_pItem->next = NULL;
        arg_pList->tail->next = arg_pItem;
        arg_pList->tail = arg_pList->tail->next;
    }

    arg_pList->count++;
}

tw_list_node_p tw_list_removeitem(tw_list_p arg_pList, void *key)
{
    tw_list_node_p pPrevItem = NULL;
    tw_list_node_p pItem = NULL;

    if( arg_pList->nodecmp == NULL )
        return NULL;

    pItem = arg_pList->head;
    while( pItem )
    {
        if( !arg_pList->nodecmp(pItem, key) )
        {
            if( pPrevItem == NULL )
            {
                /* this first item */
                arg_pList->head = pItem->next;
            }
            else
            {
                pPrevItem->next = pItem->next;
            }

            if( arg_pList->tail == pItem )
            {
                /* set the right tail pointer if we reach list tail */
                arg_pList->tail = pPrevItem;
            }

            pItem->next = NULL;
            arg_pList->count--;
            break;
        }
        
        pPrevItem = pItem;
        pItem = pItem->next;
    }

    return pItem;
}

tw_list_node_p tw_list_removehead(tw_list_p arg_pList)
{
    tw_list_node_p pItem = NULL;

    if( arg_pList->count != 0 )
    {
        pItem = arg_pList->head;
        arg_pList->head = arg_pList->head->next;
        arg_pList->count--;
        pItem->next = NULL;

        if ( pItem == arg_pList->tail )
           arg_pList->tail = arg_pList->head;
    }

    return pItem;
}

inline tw_list_node_p tw_list_gethead(tw_list_p arg_pList)
{
    return arg_pList->head;
}

inline tw_list_node_p tw_list_remove_head_to_end(tw_list_p arg_pList)
{
    tw_list_node_p pItem;

    if (arg_pList->count == 0) {
        pItem = NULL;
    } else if (arg_pList->count == 1) {
        pItem = arg_pList->head;
    } else {
        pItem = arg_pList->head;
        arg_pList->head = arg_pList->head->next;

        pItem->next = NULL;
        arg_pList->tail->next = pItem;
        arg_pList->tail = pItem;
    }

    return pItem;
}

void tw_list_merge(tw_list_p arg_pList, tw_list_p tail_pList)
{
    if (tail_pList->count == 0) {
        return;
    }

    if (arg_pList->count == 0) {
        arg_pList->count = tail_pList->count;
        arg_pList->tail = tail_pList->tail;
        arg_pList->head = tail_pList->head;
    } else {
        arg_pList->count += tail_pList->count;
        arg_pList->tail->next = tail_pList->head;
        arg_pList->tail = tail_pList->tail;
    }

    tail_pList->count = 0;
    tail_pList->head = tail_pList->tail = NULL;
}

